package com.atguigu.bean;

import com.atguigu.anno.Bean;
import com.atguigu.anno.Di;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationApplicationContext  implements ApplicationContext{

    // 创建map集合，放bean对象
    private Map<Class,Object> beanFactory = new HashMap<>();
    private static String rootPath;

    // 返回对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }


    // 创建当前类的有参数构造,传递包路径,设置包扫描规则
    // 当前包及其子包,哪个类有@Bean注解,把这个类通过反射实例化
    public AnnotationApplicationContext(String basePackage) {
        // com.atguigu
        try {
             // 1.将.转换为斜线
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            // 2.获取包绝对路径
            Enumeration<URL> urls
                    = Thread.currentThread().getContextClassLoader()
                                            .getResources(packagePath);
            while (urls.hasMoreElements()){
                URL url = urls.nextElement();
                // /D:/IdeaProjects/guigu/spring6/atguigu-spring/target/classes/com\atguigu
                String filePath = URLDecoder.decode(url.getFile(),"utf-8");
                System.out.println(filePath);
                // 获取到了绝对路径 /D:/IdeaProjects/guigu/spring6/atguigu-spring/target/classes/
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                // 现在这个文件就在com/atguigu这个路径,里面有anno等文件夹和文件
                loadBean(new File(filePath));
            }

        }catch (Exception e){
            throw new RuntimeException(e);
        }

        // loadDi 属性注入
        loadDi();
    }



    // 包扫描过程，实例化
    private void loadBean(File file) throws Exception {
        // 1.判断当前是否文件夹
        if(file.isDirectory()){
            // 2.获取文件夹里面所有内容
            File[] childrenFiles = file.listFiles();
            // 3.判断这个文件夹下面是否为空,如果为空直接结束
            if(childrenFiles == null || childrenFiles.length == 0) {
                return;
            }
            // 4.遍历这些文件
            for (File childrenFile : childrenFiles) {
                // 5.如果当前文件是一个文件夹
                if(childrenFile.isDirectory()){
                    loadBean(childrenFile);
                }else {
                    // 6.如果是一个文件,我们就得到了这个文件的包路径
                    String pathWithClass =
                            childrenFile.getAbsolutePath().substring(rootPath.length() - 1);
                    // 7.判断当前文件是否为class对象
                    if(pathWithClass.contains(".class")){
                        // 如果是class对象,我们将.class去掉。将\\全部换成.
                        String allName = pathWithClass.replaceAll("\\\\", ".")
                                .replace(".class", "");
                        // 获取当前这个类的class对象
                        Class<?> clazz = Class.forName(allName);
                        // 判断当前类是不是一个接口
                        if(!clazz.isInterface()){
                            // 判断类上面是否有注解 @Bean
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation != null) {
                                // 创建当前类
                                Object instance = clazz.getConstructor().newInstance();
                                // 将当前创建出来的这个类放到map中
                                // 如果当前的这个实现类,有接口的话,在map中使用接口作为key
                                // 如果当前的这个实现类,没有接口的话,在map中使用自己作为key
                                if(clazz.getInterfaces().length > 0){
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                }else {
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }



    private void loadDi() {
          // 获取map中的所有的类
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry : entries) {
            // 获取当前map容器中的类
            Object value = entry.getValue();
            // 得到当前类的class对象
            Class<?> clazz = value.getClass();
            // 获取每个对象属性获取到
            Field[] declaredFields = clazz.getDeclaredFields();
            // 遍历这些属性
            for (Field field : declaredFields) {
                // 获取属性上的注解
                Di annotation = field.getAnnotation(Di.class);
                if(annotation != null){
                    // 设置当前属性是可操作的
                    field.setAccessible(true);
                    // 如果有注解，把对象进行设置（注入）
                    try {
                        field.set(value,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }

            }
        }
    }
}
